实现CVE-2016-3842的堆喷
前言
看到论坛有大牛分析了这个CVE-2016-3842的利用方法,我之前也对这个漏洞的堆喷做了一些笔记,这里分享一下。首先要先感谢一下某因幡和Retme两位大神,在研究这个漏洞期间遇到不少的问题,他们都一一给我解答了,这里对他们表示再次感谢。
漏洞介绍
这个漏洞是GPU中的一个UAF漏洞,是由于race condition 造成的。在GPU驱动中提供了一个ioctl命令IOCTL_KGSL_GPUMEM_ALLOC ,这个命令可以让用户去分配一块GPU共享内存。当一个线程调用这个ioctl之后,程序会创建一个kgsl_mem_entry的结构体用来描述一块已经分配好的内存。与此同时函数kgsl_mem_entry_attach_process会通过调用idr_alloc函数给kgsl_mem_entry分配一个ID。在这个时候,另外一个线程调用 IOCTL_KGSL_GPUMEM_FREE_ID 的ioctl命令去释放这个内存块,如果能在分配函数完成之前将这个kgsl_mem_entry释放掉,那么直接就造成了UAF了。
再来看一下官方介绍:
If we add the mem entry pointer in the process idr and rb tree too early, other threads can do operations on the entry by guessing the ID or GPU address before the object gets returned by the creating operation.
POC
因为每在内核分配的第一个kgsl_mem_entry所分配的ID为1,所以可以准确释放刚分配好的内存块。
代码如下:
实际堆喷中遇到的问题
1. 使用seccomp被拦截
用的是seccomp这个syscall来进行堆喷,seccomp被拦截了,如下:
在提示过后,查看android内核源码,发现并不需要任何权限,但是要在使用之前设置一个Admin的进程属性来绕过。
2. 堆喷如何判断是否成功
调试漏洞时,选择自己能改内核代码的机器来调。printk就能直观显示是否成功。
还有相对高级的方法是,可以在喷的内容中做一些标记,比如deadbeef,这样喷上去之后在崩溃,crash寄存器中会有反馈。
建议优先用printk。
3. 崩溃日志的疑问
按漏洞的原理来看,崩溃时候的函数应该是顺着IOCTL_KGSL_MAP_USER_MEM这个ioctl的,但是为什么崩溃信息里为什么会是这个函数导致崩溃的呢?看这个崩溃信息,让我有点迷糊了,不知道准确的触发时机是什么时候了,是不是在我的机器上就是这个函数触发的崩溃?
ShenDi大神的说法是这样的, “触发时机是有2~3处的,这取决于free的时机,你没法控制这个时机,但是如果你能喷堆成功,就能保证这2~3个时机都不会崩溃。”后来经我的验证,是因为我用的是5X的机器,而他用的是6P,崩溃信息确实不一样。
4. 关于堆喷结构体sock_filter
对于sock_filter,如果不加修饰的随便定义这个结构体去喷,虽然它也会被kmalloc,只不过之后校验参数的时候 会失败,然后会被free掉,这个值得注意。因为如果seccomp失败的话,调用一次和调用一万次是没有区别的,kmalloc会永远落在同一个object上。
了解了以上这些,就可以开始尝试堆喷了,堆喷代码如下:
内核print堆喷信息如下:
经过不断的尝试,最后堆喷成功:
总结
折腾了很久,终于堆喷成功。多看代码,多尝试,多坚持。
本文由看雪论坛 Ericky 原创
转载请注明来自看雪社区
热门阅读